package five.philosophers;


import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author mateusz.ledzewicz
 */
public class Philosopher extends Thread{
    private static int sleepTime = 10;
    private static int eatTime = 20;
    
    private long runTime = 0;
    
    private int id;
    public int state = 0;
    public int eatCounter = 0;
    public int waitTime;
    private Fork greaterFork;
    private Fork smallerFork;
    
    public Philosopher(){
        this.id=IdMemory.getNewPhilosopherId();
        ArrayList<Integer> forks = new ArrayList<Integer>();
        forks.add(this.id%(FivePhilosophers.numberOfPhilosophers));
        forks.add((this.id+1)%(FivePhilosophers.numberOfPhilosophers));
        
        this.greaterFork = IdMemory.getFork(Collections.max(forks));
        this.smallerFork = IdMemory.getFork(Collections.min(forks));
       
    }
    
    @Override
    public void run(){
        Long beginTime = System.nanoTime();
        for(int i =0; i<FivePhilosophers.numberOfCycles; i++){
            this.think();
            this.tryToEat();
            this.eat();
        }
        Long endTime = System.nanoTime();
        this.runTime = (endTime-beginTime)/1000000;
        this.logMajor("End");
        
    }
    
    public void think(){
        this.state = 1;
        this.log("Start thinking");
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException ex) {
            Logger.getLogger(Philosopher.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.log("End thinking");
        this.state = 0;
    }
    
    public void tryToEat(){
        this.log("Try eat");
        Long beginTime = System.nanoTime();
        try {
            this.smallerFork.lift();
            this.greaterFork.lift();
        } catch (InterruptedException ex) {
            Logger.getLogger(Philosopher.class.getName()).log(Level.SEVERE, null, ex);
        }
        Long endTime = System.nanoTime();
        this.waitTime+=(endTime-beginTime)/1000000;
        this.state = 2;
        this.eatCounter++;  
    }
    
    public String getStatistic(){
        return this.id + ";" + this.eatCounter + ";" + this.getAvgWaitTime()+";" + this.runTime;
    }
    
    public double getAvgWaitTime(){
        return this.waitTime/this.eatCounter;
    }
    
    public void eat(){
      this.log("Start eating");
      try {
            Thread.sleep(eatTime);
      } catch (InterruptedException ex) {
            Logger.getLogger(Philosopher.class.getName()).log(Level.SEVERE, null, ex);
      }
      this.log("Release forks");
      this.state = 0;
      this.greaterFork.put();
      this.smallerFork.put();
      this.log("End eating");
      
    }
    
    
    public String toString(){
        return "Philosopher " + this.id + "\n   Fork greater=" + greaterFork + "\n  Fork smaller=" + smallerFork;
    }
    
    public void log(String message){
        //System.out.println("Philosopher " + this.id + " " + message);
    }
    
    public void logMajor(String message){
        System.out.println("Philosopher " + this.id + " " + message);
    }
}
